package sm;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.hyperic.sigar.NetInterfaceConfig;
import org.hyperic.sigar.SigarException;

public class JMon {
    static {
        DOMConfigurator.configure("log4j.xml");
    }
    SMCPU cpu;
    SMMemory memory;
    ArrayList<Long> pids;
    Logger log;

    boolean systemCPU;
    boolean systemMemoryUsed;
    boolean systemMemoryFree;
    SimpleDateFormat dateFormat;

    private boolean mNet;
    private boolean mDisk;

    private SMNet net;

    String split;

    int sleepTime;
    private static final String net_read="-I" ;
    private static final String net_send="-O" ;
    String [] netKa ;
    public JMon() {
        log = Logger.getLogger(this.getClass());
        cpu = new SMCPU();
        memory = new SMMemory();
        pids = new ArrayList<Long>();

        systemCPU = true;
        systemMemoryFree = true;
        systemMemoryUsed = true;

        split = ", ";
        sleepTime = 1000;
        dateFormat = new SimpleDateFormat("yyyy-MM-dd" + split + "hh:mm:ss");

        this.mDisk = false;
        this.mNet = false;
        net = new SMNet();
    }

    public void addPidByName(String name) throws SigarException {
        long[] ps = cpu.getPidByName(name);
        for (long pid : ps) {
            pids.add(pid);
        }
    }

    public void addPidByPid(long pid) {
        this.pids.add(pid);
    }

    public void addPidByPort(long port) throws SigarException {
        this.pids.add(cpu.getPidByPort(port));
    }

    public String getPidInfo(long pid) throws SigarException {
        return cpu.getPidName(pid) + " arg: " + cpu.getArgsOfPidOneLine(pid);
    }

    public void writePidsInfo() throws SigarException {
        for (long pid : pids) {
            log.info("### pid " + pid +   " ### "+getPidInfo(pid));
        }
    }
    public void writeNetInfo() throws SigarException{
        String[] nets = net.getNetLinks();
        String head = "" ;
        for (String s : nets) {
            log.info("###net "+s+" ### "+ s + ", ip" + net.getIP(s) );
        }
    }
    protected String getPidCPUMemoryInfoHeadLine() throws SigarException {

        String oneLineLog = "";
        if (systemCPU)
            oneLineLog += "system-Cpu-used" + split;
        if (systemMemoryFree)
            oneLineLog += "system-memory-free" + split;
        if (systemMemoryUsed)
            oneLineLog += "system-memory-used" + split;
        for (long pid : pids) {
            oneLineLog += pid + "-cpu-used" + split;
            oneLineLog += pid + "-memory-vm" + split;
            oneLineLog += pid + "-memory" + split;
        }
        return oneLineLog;
    }
    protected String getNetHeadLine() throws SigarException{
        String head = "" ;
        for(int i=0; i<netKa.length; ++i){
            String s= netKa[i] ;
            if( i==0 )
                head += s +net_read+"_" + net.getIP(s) ;
            else 
                head +=split+ s +net_read+"_" + net.getIP(s) ;
            head+=split ;
            head += s +net_send+"_" + net.getIP(s) ;

        }
        return head ;
    }
    public void initNetMap() throws SigarException {
        this.netKa = net.getNetLinks();
    }

    protected String getPidCPUMemoryInfoOneLine() throws SigarException {
        String oneLineLog = "";
        DecimalFormat df1 = new DecimalFormat("##.##");
        Date data = Calendar.getInstance().getTime();
        long dateTime = data.getTime();

        oneLineLog += dateTime + split;
        oneLineLog += dateFormat.format(data) + split;

        if (systemCPU)
            oneLineLog += cpu.getSystemCPUUsageAsString() + split;
        if (systemMemoryFree)
            oneLineLog += memory.getSystemFreeMemory() + split;
        if (systemMemoryUsed)
            oneLineLog += memory.getSystemUsedMemory() + split;
        for (long pid : pids) {
            oneLineLog += df1.format(cpu.getPidCPUUsage(pid)) + split;
            oneLineLog += memory.getPidVMMemory(pid) + split;
            oneLineLog += memory.getPidMemory(pid) + split;
        }
        return oneLineLog;
    }
    protected String getNetStateOneLine() throws SigarException{
        String oneline ="" ;
        for(int i=0; i<netKa.length; ++i){
            String name= netKa[i] ;
            long rspeed = net.getNetLinkReadM(name) ;
            if( i==0 )
                oneline += rspeed ;
            else
                oneline += split + rspeed ;
            oneline += split ;
                       long wspeed = net.getNetLinkSendM(name)   ;
            oneline += wspeed ;
        }
        return oneline;
    }
    protected void sleep(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run() throws SigarException {
        writePidsInfo();
        initNetMap() ;
        if( mNet)  writeNetInfo() ;
        String head= getPidCPUMemoryInfoHeadLine () ;
        if( mNet){
//            head+=split ;
            head+=getNetHeadLine() ;
        }
        log.info(head);
        
        while (true) {
            String oneLine = getPidCPUMemoryInfoOneLine() ;
            if( mNet ){
                oneLine += getNetStateOneLine() ;
            }
            sleep(sleepTime);
            log.info(oneLine);
        }
    }

    public boolean isSystemMemoryFree() {
        return systemMemoryFree;
    }

    public void setSystemMemoryFree(boolean systemMemoryFree) {
        this.systemMemoryFree = systemMemoryFree;
    }

    public boolean isSystemCPU() {
        return systemCPU;
    }

    public void setSystemCPU(boolean systemCPU) {
        this.systemCPU = systemCPU;
    }

    public boolean isSystemMemoryUsed() {
        return systemMemoryUsed;
    }

    public void setSystemMemoryUsed(boolean systemMemoryUsed) {
        this.systemMemoryUsed = systemMemoryUsed;
    }

    public String getSplit() {
        return split;
    }

    public void setSplit(String split) {
        this.split = split;
    }

    public void addLogFile(String file) {
        FileAppender fp = Log4jUtil.getFileAppender(file);
        log.addAppender(fp);
    }

    public SimpleDateFormat getDateFormat() {
        return dateFormat;
    }

    public void setDateFormat(SimpleDateFormat dateFormat) {
        this.dateFormat = dateFormat;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = new SimpleDateFormat(dateFormat);
    }

    public void monitorDiskIO(Long pid) throws Exception {
        throw new Exception("Did not support this option at current");

    }

    public void monitorDiskIO() throws Exception {
        throw new Exception("Did not support this option at current");

    }

    public void monitorNetIO(Long pid) throws Exception {
        throw new Exception("Did not support this option at current");
    }

    public void monitorNetIO() {
        this.mNet = true;
    }
    public void setSleepTime( int s ){
        this.sleepTime = s ;
    }
    public String help() {
        String help = "" + "at current, just support monitor memory and cpu and full net/disk i/o"
                + "\n\t --pid, -p, the pid you want to monitor"
                + "\n\t --output, -o, the file of record date, default is jmon.log"
                + "\n\t --pname, -pn, monitor the pids that with the name"
                + "\n\t --split, -s, the split string betin each colum value, default is ', '"
                + "\n\t  --port, find pid by http-tcp port, and monitor the pid"
                + "\n\t --disk-io, monitor the disk io of the system"
                + "\n\t --net-io, monitor the net io of the system"
                + "\n\t --dateformat, the set date format in the --output file, default is "
                + "\n\t --sleep, sleep time between two monitor time";
        return help;
    }

    public static void main(String[] args) throws Exception {
        JMon r = new JMon();
        if (args.length == 0) {
            System.err.println(r.help());
            return;
        }
        for (int i = 0; i < args.length; ++i) {
            String option = args[i];
            if (option.equals("--pid") || option.equals("-p")) {
                ++i;
                r.addPidByPid(Long.parseLong(args[i]));
            } else if (option.equals("--output") || option.equals("-o")) {
                ++i;
                r.addLogFile(args[i]);
            } else if (option.equals("--pname") || option.equals("-pn")) {
                ++i;
                r.addPidByName(args[i]);
            } else if (option.equals("--split") || option.equals("-s")) {
                ++i;
                r.setSplit(args[i]);
            } else if (option.equals("--port")) {
                ++i;
                r.addPidByPort(Long.parseLong((args[i])));
            } else if (option.equals("--disk-io")) {
                r.monitorDiskIO();
            } else if (option.equals("--net-io")) {
                r.monitorNetIO();
            } else if (option.equals("--sleep")) {
                ++i;
                r.setSleepTime(Integer.parseInt(args[i]));
            } 
            else {

                System.err.println(r.help());
                throw new Exception("did not know parame: " + option);
            }
        }
        r.run();
    }
}
